package server

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/elastic/go-elasticsearch/v7/esapi"
	"github.com/google/uuid"
	"log"
	"strconv"
	"sync"
	"tourism-app/common/cron"
	"tourism-app/common/models/es"
	"tourism-app/common/models/mysql"
	"tourism-app/common/pkg"
	"tourism-app/common/proto/user"
	"tourism-app/common/viper"
)

func Login(in *user.LoginReq) (*user.LoginResp, error) {
	u := &mysql.User{}
	users, err := u.FindUserByMobile(in.Mobile)
	if err != nil {
		return nil, errors.New("查询失败")
	}
	if users.ID == 0 {
		return nil, errors.New("还未注册")
	}
	if in.Password != users.Password {
		return nil, errors.New("密码错误")
	}
	claims := pkg.CustomClaims{ID: users.ID}
	token, err := pkg.NewJWT(viper.Conf.Jwt.SecretKey).CreateToken(claims)
	if err != nil {
		return nil, errors.New("token生成失败")
	}
	return &user.LoginResp{Greet: token}, nil
}

func ScenicToEs(in *user.ScenicToEsReq) (*user.ScenicToEsResp, error) {
	var (
		list []*mysql.Scenic
		wg   sync.WaitGroup
	)
	mysql.DB.Find(&list)
	for _, scenic := range list {
		wg.Add(1)

		go func(scenic *mysql.Scenic) {
			defer wg.Done()

			// Build the request body.
			data, err := json.Marshal(scenic)
			if err != nil {
				log.Fatalf("Error marshaling document: %s", err)
			}

			// Set up the request object.
			req := esapi.IndexRequest{
				Index:      "scenic",
				DocumentID: strconv.Itoa(int(scenic.ID)),
				Body:       bytes.NewReader(data),
				Refresh:    "true",
			}

			// Perform the request with the client.
			res, err := req.Do(context.Background(), es.Es)
			if err != nil {
				log.Fatalf("Error getting response: %s", err)
			}
			defer res.Body.Close()

		}(scenic)
	}
	wg.Wait()

	return &user.ScenicToEsResp{}, nil
}
func SearchScenic(in *user.SearchScenicReq) (*user.SearchScenicResp, error) {
	query := map[string]interface{}{}
	var buf bytes.Buffer
	if in.KeyWord == "" {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"match_all": map[string]interface{}{},
			},
		}
	} else {
		query = map[string]interface{}{
			"query": map[string]interface{}{
				"match": map[string]interface{}{
					"name": in.KeyWord,
				},
			},
		}
	}

	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		log.Fatalf("Error encoding query: %s", err)
	}

	// Perform the search request.
	res, err := es.Es.Search(
		es.Es.Search.WithContext(context.Background()),
		es.Es.Search.WithIndex("scenic"),
		es.Es.Search.WithBody(&buf),
		es.Es.Search.WithTrackTotalHits(true),
		es.Es.Search.WithPretty(),
	)
	if err != nil {
		log.Fatalf("Error getting response: %s", err)
	}
	defer res.Body.Close()

	var r map[string]interface{}
	json.NewDecoder(res.Body).Decode(&r)
	// Print the response status, number of results, and request duration.
	log.Printf(
		"[%s] %d hits; took: %dms",
		res.Status(),
		int(r["hits"].(map[string]interface{})["total"].(map[string]interface{})["value"].(float64)),
		int(r["took"].(float64)),
	)
	fmt.Println(r)
	var List []*user.SearchScenic
	// Print the ID and document source for each hit.
	for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
		source := hit.(map[string]interface{})["_source"].(map[string]interface{})
		fmt.Println(source)
		fmt.Println(11111111)
		List = append(List, &user.SearchScenic{
			Id:      int64(source["ID"].(float64)),
			Name:    source["name"].(string),
			Address: source["address"].(string),
		})

	}
	return &user.SearchScenicResp{List: List}, nil
}
func ScenicItem(in *user.ScenicItemReq) (*user.ScenicItemResp, error) {
	s := &mysql.Scenic{}
	scenic, err := s.FindScenicById(in.Id)
	if err != nil {
		log.Println(err.Error())
		return nil, errors.New("查询失败")
	}
	a := &mysql.Admission{}
	id, err := a.FindAdmissionById(in.Id)
	if err != nil {
		return nil, err
	}
	var List []*user.ScenicItem
	for _, m := range scenic {
		List = append(List, &user.ScenicItem{
			Id:      int64(m.ID),
			Name:    m.Name,
			Address: m.Address,
		})
	}
	for _, ad := range id {
		List = append(List, &user.ScenicItem{
			TicketName: ad.TicketName,
			Price:      float32(ad.Price),
			Num:        int64(ad.Num),
		})
	}
	return &user.ScenicItemResp{List: List}, nil
}
func CreateOrder(in *user.CreateOrderReq) (*user.CreateOrderResp, error) {
	r := &mysql.RealName{}
	id, err := r.FindRealNameByUserId(in.UserId)
	if err != nil {
		return nil, errors.New("查询失败")
	}
	if id.ID == 0 {
		return nil, errors.New("用户还未实名")
	}
	s := &mysql.Scenic{}
	a := &mysql.Admission{}
	sids, err := s.FindScenicByIds(in.ScenicId)
	if err != nil {
		return nil, errors.New("查询失败")
	}
	if sids.ID == 0 {
		return nil, errors.New("没有该景点")
	}
	aIds, err := a.FindAdmissionByIds(in.AdmissionId)
	if err != nil {
		return nil, errors.New("查询失败")
	}
	if aIds.ID == 0 {
		return nil, errors.New("没有这种票")
	}
	o := &mysql.Order{}
	i := &mysql.Insure{}
	if int(in.Num) > a.Num {
		return nil, errors.New("票量不足")
	}
	orderSn := uuid.New().String()

	price := float64(in.Num) * aIds.Price
	if in.ScenicId != 1 {
		price += i.Price
		if !o.UpdatePrice(price) {
			return nil, errors.New("修改失败")
		}
	}
	o = &mysql.Order{
		UserId:      in.UserId,
		OrderSn:     orderSn,
		ScenicId:    in.ScenicId,
		AdmissionId: in.AdmissionId,
		Num:         in.Num,
		Price:       price,
		Status:      0,
	}
	tx := mysql.DB
	tx.Begin()
	err = o.CreateOrder()
	if err != nil {
		tx.Rollback()
		return nil, errors.New("创建失败")
	}

	count := a.Num - int(in.Num)
	err = a.UpdateNum(in.AdmissionId, count)
	if err != nil {
		return nil, errors.New("库存修改失败")
	}

	fmt.Println("创建成功")

	tx.Commit()

	float := strconv.FormatFloat(o.Price, 'f', 1, 64)
	pays := pkg.AliPays()
	pay := pays.Pay(a.TicketName, orderSn, float)
	cron.GetOrderStatus(orderSn)
	return &user.CreateOrderResp{Greet: pay}, nil
}
func RealName(in *user.RealNameReq) (*user.RealNameResp, error) {
	r := &mysql.RealName{}
	card, err := r.CheckRealNameByUserIdAndIdCard(int(in.UserId), in.IdCard)
	if err != nil {
		return nil, errors.New("查询失败")
	}
	if card.ID != 0 {
		return nil, errors.New("该用户已实名或该实名信息已被使用")
	}
	if len(in.IdCard) < 18 {
		return nil, errors.New("身份证号长度不符")
	}

	/*if !pkg.Realname(in.IdCard, in.Name) {
		return nil, errors.New("实名不正确")
	}*/
	r = &mysql.RealName{
		UserId: in.UserId,
		Name:   in.Name,
		IdCard: in.IdCard,
	}
	err = r.CreateRealname()
	if err != nil {
		return nil, errors.New("创建实名信息失败")
	}
	return &user.RealNameResp{Greet: "实名成功"}, nil
}
func UpdateOrderStatus(in *user.UpdateOrderStatusReq) (*user.UpdateOrderStatusResp, error) {
	o := &mysql.Order{}
	err := o.UpdateOrderStatus(in.OrderSn, int(in.Status))
	if err != nil {
		return nil, errors.New("修改失败")
	}
	return &user.UpdateOrderStatusResp{Greet: "修改成功"}, nil

}
func OrderList(in *user.OrderListReq) (*user.OrderListResp, error) {
	o := &mysql.Order{}
	id, err := o.FindOrderById(in.UserId)
	if err != nil {
		return nil, err
	}
	var List []*user.OrderList
	for _, order := range id {
		List = append(List, &user.OrderList{
			UserId:      order.UserId,
			OrderSn:     order.OrderSn,
			ScenicId:    order.ScenicId,
			AdmissionId: order.AdmissionId,
			Num:         order.Num,
			Price:       float32(order.Price),
			Status:      order.Status,
			InsureId:    order.InsureId,
		})
	}
	return &user.OrderListResp{List: List}, nil
}
func Refund(in *user.RefundReq) (*user.RefundResp, error) {
	o := &mysql.Order{}
	err := o.UpdateOrderStatus(in.OrderSn, int(in.Status))
	if err != nil {
		return nil, err
	}
	return &user.RefundResp{Greet: "成功"}, nil
}
